/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.kramerius.rukopisyimport.foxml;

import cz.mzk.kramerius.commons.Dom4jUtils;
import cz.mzk.kramerius.imports.FoxmlHelper;
import cz.mzk.kramerius.rukopisyimport.sourceData.TeiDocument;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dom4j.Attribute;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;
import org.dom4j.XPath;

/**
 *
 * @author Martin Řehánek (rehan at mzk.cz)
 */
public class MonographInternalPartsBuilder {

    private static final Logger logger = Logger.getLogger(MonographInternalPartsBuilder.class.getName());
    private static final XPath titleXpath = Dom4jUtils.createXPath("./tei:title|./tei:incipit");
    private static final XPath idXpath = Dom4jUtils.createXPath("./@n");
    private static final XPath pageFromXpath = Dom4jUtils.createXPath("./tei:locus/@from");
    private static final XPath pageToXpath = Dom4jUtils.createXPath("./tei:locus/@to");
    private static final XPath pageAtXpath = Dom4jUtils.createXPath("./tei:locus/@target");
    private static final XPath noteXpath = Dom4jUtils.createXPath("./tei:note");
    private final TeiDocument tei;
    private final InternalPartBuilder internalPartBuilder;

    public MonographInternalPartsBuilder(File conversionDir, TeiDocument tei, List<Page> pageList) {
        this.tei = tei;
        Pages pages = new Pages(pageList);
        this.internalPartBuilder = new InternalPartBuilder(pages, conversionDir);
    }

    public List<InternalPart> createInternalParts() {
        List msItems = tei.findMsItems();
        return createInternalParts(msItems);
    }

    private List<InternalPart> createInternalParts(List<Element> msItems) {
        List<InternalPart> internalParts = new ArrayList<InternalPart>();
        for (Element msItem : msItems) {
            String title = "";
            try {
                title = stringValueOrEmptyString(titleXpath.selectSingleNode(msItem));
                String[] extent = findPageExtent(msItem);
                String note = stringValueOrEmptyString(noteXpath.selectSingleNode(msItem));
                InternalPart internalPart = internalPartBuilder.createInternalPart(FoxmlHelper.generateUuid(), extent[0], extent[1], note, title);
                internalParts.add(internalPart);
            } catch (Exception e) {
                String titlePart = title.substring(0, Math.min(title.length(), 20));
                logger.log(Level.SEVERE, "\"{0}\":{1}, ommiting msItem", new Object[]{titlePart, e.getMessage()});
            }
        }
        return internalParts;
    }

    private String stringValueOrEmptyString(Node node) {
        if (node == null) {
            return String.valueOf("");
        } else if (node instanceof Element) {
            return textTrimWithChildrenContents((Element) node);
        } else {
            return node.getText();
        }

    }

    private String textTrimWithChildrenContents(Element root) {
        StringBuilder builder = new StringBuilder();
        for (Object son : root.content()) {
            if (son instanceof Text) {
                builder.append(((Text) son).getText().trim());
            } else if (son instanceof Element) {
                builder.append(" ");
                builder.append(textTrimWithChildrenContents((Element) son));
                builder.append(" ");
            }
        }
        return builder.toString();
    }

    private String[] findPageExtent(Element msItem) throws Exception {
        String pageFrom = findPageId(msItem, pageFromXpath, "from");
        String pageTo = findPageId(msItem, pageToXpath, "to");
        String pageAt = findPageId(msItem, pageAtXpath, "target");
        if (pageFrom != null && pageTo != null) {
            return new String[]{pageFrom, pageTo};
        } else if (pageAt != null) {
            return new String[]{pageAt, pageAt};
        } else {
            throw new Exception("Cannot find page extent");
        }
    }

    private String findPageId(Element msItem, XPath xpath, String string) {
        Attribute attribute = (Attribute) xpath.selectSingleNode(msItem);
        if (attribute == null) {
            return null;
        } else {
            return attribute.getText();
        }
    }
}
